import settings from "./settings"
import request from "../../requestV2"
import { Vec3 } from "./vec3";
import Promise from "../../PromiseV2";
import { AddData, SavedData } from "./data";
import { HypixelModAPI } from "../../HypixelModAPI";

const mc = Client.getMinecraft();
const ModLoader = Java.type("net.minecraftforge.fml.common.Loader");
/**
 * 是否装了某个mod
 * @param {string} ModName mod名
 * @returns {boolean} 如果有装则返回 true
 */
export function isModLoaded(ModName) {
    return ModLoader.isModLoaded(ModName);
}


export const MessageID = {
    Help : 25600,
    TabCompletion : 25601,
    FishingSetting : 25602,
    WishingCompass : 25603,
    Cake : 25604,
    UpdateAuctions : 25605,
    UpdateVersion : 25606,
    UpdateNeuRepo : 25607,
    RareDropSetting : 25608,
    GardenRotationAngle : 25609
}


/**
 * 获取玩家当前坐标
 * @returns {Vec3}
 */
export function getPlayerPos() {
    return new Vec3(Player.getX(),Player.getY(),Player.getZ());
}

/**
 * 获取实体当前坐标
 * @param {Entity} entity
 * @returns {Vec3}
 */
export function getEntityPos(entity) {
    return new Vec3(entity.getX(),entity.getY(),entity.getZ());
}

/**
 * 将Vec3i转换成Vec3
 * @param {Vec3i} Vec3i 
 * @returns {Vec3}
 */
export function Vec3iToVec3(Vec3i) {
    return new Vec3(Vec3i.x,Vec3i.y,Vec3i.z);
}

//关gui时清掉Tab键补全时留下的信息
export let clearTabCompletionMessage = {
    clear : false,
    clearNext() {
        this.clear = true;
    }
}
register("guiClosed", () => { 
    if(clearTabCompletionMessage.clear) {
        ChatLib.deleteChat(MessageID.TabCompletion);
        clearTabCompletionMessage.clear = false;
    }
})

let pingTime = 0;
register("worldLoad",()=>{pingTime = 0;pingCheck.unregister()})
/**
 * ping (hypixel)
 */
export function ping() {
    if(pingTime) return;
    pingTime = Date.now();
    pingCheck.register();
    ChatLib.command("asfwregasdgerhfcgxfghqweehsdftjrtsthffgjnvcrty");
}
let pingCheck = register("chat", (event) => {
    cancel(event);
    let ping = Date.now() - pingTime
    ChatLib.chat("§6§l[BZA] §b当前延迟: " + (ping > 300 ? "§c" : (ping > 250 ? "§e" : "§a")) + ping);
    pingCheck.unregister();
    pingTime = 0;
}).setCriteria(`Unknown command. Type "/help" for help. ('asfwregasdgerhfcgxfghqweehsdftjrtsthffgjnvcrty')`).unregister();

/**
 * 服务器tick相关时间获取和触发器注册
 */
export const ServerTick = {
    time : Date.now(),
    tick : 0,
    getTime() {
        return this.time;
    },
    getTick() {
        return this.tick;
    },

    triggers : [],
    /**
     * 创建一个每几个tick就触发一次的触发器
     * @param {Number} tick 
     * @param {Function} func 
     */
    register(tick, func) {
        this.triggers.push({
            tickleft : tick,
            totaltick : tick,
            func : func
        });
    }
}
register("packetReceived", () => {
    ServerTick.time += 50;
    ServerTick.tick++;
    ServerTick.triggers.forEach(trigger => {
        trigger.tickleft--;
        if(trigger.tickleft <= 0) {
            trigger.func();
            trigger.tickleft = trigger.totaltick;
        }
    })
}).setFilteredClass(net.minecraft.network.play.server.S32PacketConfirmTransaction)
register("worldLoad",() => {ServerTick.time = Date.now();ServerTick.tick = 0;})

/**stripRank
 * @param {string} rankedPlayer 
 */
export function stripRank(rankedPlayer) {
	return rankedPlayer.removeFormatting().replace(/\[[\w+\+-]+] /, "")
}

/**
 * 往文本中插入文本
 * @param {string} source 原文本
 * @param {number} start 插入的位置
 * @param {string} newStr 插入的内容
 * @returns {string}
 */
export function insertStr(source, start, newStr) { return source.slice(0, start) + newStr + source.slice(start) }

/**时间数字转文本
 * @param {number} time 单位毫秒
 * @returns {string} hh:mm:ss | ss.ms
 */
export function timedisplay(time) {
    let h = null;
    let m = null;
    let s = null;
    if(time > 60*60*1000){
        h = parseInt(time/3600000)
        m = parseInt(time/60000) - (h * 60)
        s = parseInt(time/1000) - (h * 3600) - (m * 60)
    }else if (time > 60*1000) {
        m = parseInt(time/60000)
        s = parseInt(time/1000) - (m * 60)
    }else{
        s = parseInt(time/1000)
    }
    ms = time - s * 1000;
    if(ms < 100){ms = "0" + ms}
    if(h){
        return h + ":" + m + ":" + s;
    }else if(m){
        return m + ":" + s + "s";
    }else{
        return s + "." + ms + "s";
    }
}

/**remove emoji from string
 * @param {string} string 
 * @returns {string}
 */
export function RemoveEmoji (string) {
    let emoji = /[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/ig;
    return string.replace(emoji,'');
}

/**
 * 从聊天栏的消息中获取点击触发的指令,若找到多个则返回第一个
 * @param {ForgeTClientChatReceivedEvent} event 消息的 event
 * @param {string} message 要获取指令的那部分
 * @returns {string | null} 点击执行的指令,若没有则返回 null
 */
export function getcmdfromchat(event,message){
    let TextComponents = new Message(event).getMessageParts().filter(e => e.toString().includes(message) && e.getClickAction() == "run_command");
    if(TextComponents.length == 0) return null;
    return TextComponents[0].getClickValue();
}

export const ScoreboardLine = {
    /**
     * 在计分板中添加一行
     * @param {number} score 
     * @param {string} line 
     */
    add(score,line) {
        score = Math.floor(score);
        Scoreboard.getLines().filter(e => e.getPoints() >= score).forEach(e => e.score.func_96649_a(1));  //score.increseScore()
        Scoreboard.setLine(score,line,false);
    },
    /**
     * 设置计分板中的其中一行
     * @param {number} score 
     * @param {string} line 
     */
    set(score,line) {
        score = Math.floor(score);
        Scoreboard.setLine(score,line,true);
    },
    /**
     * 删除计分板中的其中一行
     * @param {number} score 
     */
    del(score) {
        score = Math.floor(score);
        const sidebarObjective = Scoreboard.getScoreboard().func_96539_a(1);  //scoreboard.getObjectiveInDisplaySlot()
        const Lines = Scoreboard.getLinesByScore(score);
        if(Lines.length == 0) return;
        Lines.forEach(e => {
            Scoreboard.getScoreboard().func_178822_d(e.score.func_96653_e(),sidebarObjective);  //scoreboard.removeObjectiveFromEntity()
        })
        Scoreboard.getLines().filter(e => e.getPoints() > score).forEach(e => e.score.func_96646_b(1));  //score.decreaseScore()
    }
}

/**
 * 获取当前gui内指针指着的物品
 * @param {MCTGuiScreen} gui 目前开着的gui
 * @returns {Item | null} 指着的物品,若没有则返回 null
 */
export function getItemFromGui(gui) {
    try {
        let slot = gui.getSlotUnderMouse();
        if(!slot) return null;
        let item = Player.getContainer().getItems()[slot.field_75222_d];
        if(!item) return null;
        return item;
    } catch (error) {
        return null;
    }
}

/**
 * 获取物品的Skyblock UUID
 * @param {Item} item 物品
 * @returns {string | null} 物品的UUID,若没有则返回null
 */
export function getItemUUID(item) {
    return item.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getString("uuid");
}

/**
 * 获取物品的Skyblock ID
 * @param {Item} item 物品
 * @returns {string | null} 物品的SBID,若没有则返回null
 */
export function getItemSBID(item) {
    return item?.getNBT()?.getCompoundTag("tag")?.getCompoundTag("ExtraAttributes")?.getString("id");
}

const GuiScreen = Java.type("net.minecraft.client.gui.GuiScreen");
/**
 * 复制文本到剪贴板
 * @param {string} string 要复制的文本
 * @param {string} succussMessage 成功时的提示信息
 */
export function copyToClipboard(string,succussMessage = "") {
    try {
        GuiScreen.func_146275_d(string);  //setClipboardString
        if(succussMessage) ChatLib.chat(succussMessage);
    } catch (error) {
        ChatLib.chat("§6§l[BZA] §4复制失败,报错信息如下: \n§c" + error);
    }
}

/**
 * 获取剪贴板的内容
 * @returns {string | null} 剪贴板内的文本
 */
export function getClipboard() {
    return GuiScreen.func_146277_j();  //getClipboardString
}


const Mouse = Java.type("org.lwjgl.input.Mouse");
/**
 * 移动鼠标指针到指定位置
 * @param {number} newMouseX 
 * @param {number} newMouseY 
 * @param {boolean} Grabbed 是否抓取鼠标
 */
export function mouseMove(newMouseX,newMouseY,Grabbed = false) {
    Mouse.setCursorPosition(newMouseX, newMouseY);
    Mouse.setGrabbed(Grabbed);
}

/**
 * 移动鼠标指针到指定物品的槽位
 * @param {number} slotId 槽位id
 * @param {MCTContainer} container 
 * @returns 
 */
export function moveMouseToSlot(slotId = 0, container = Player.getContainer().container) {
    try {
        if(!container) return;
        let gui = mc.field_71462_r;
        if(!gui) return;
        let slot = container.func_75139_a(slotId);
        mouseMove((gui.guiLeft + slot.field_75223_e) * Renderer.screen.getScale() + 18, (Renderer.screen.getHeight() - (gui.guiTop + slot.field_75221_f)) * Renderer.screen.getScale() - 36 + 18);
    } catch (error) {}
}


let baseString = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&*()_+-{}|:<>?";
/**
 * 随机生成一串字符,里面包含: 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~!@#$%^&*()_+-{}|:<>?
 * @param {number} length 字符串的长度
 * @returns {string} 返回的字符串
 */
export function randomString(length = 8) {
    let string = "";
    while(string.length < length) {
        string += baseString.charAt(Math.floor(Math.random() * baseString.length));
    }
    return string;
}


const JsonToNBT = Java.type("net.minecraft.nbt.JsonToNBT");
/**
 * 通过物品id创建一个ItemStack
 * @param {string} ItemID 物品的minecraft ID , 例: minecraft:stone
 * @param {number} damage 物品的损伤值 默认为0
 * @param {MCTNBTTagCompound | string} nbttag 物品的NBTtag 或 JSON格式的文本 默认为 null
 * @returns {MCTItemStack | null} 若未找到此物品则返回 null
 */
export function newItemStackFrom(ItemID,damage=0,nbttag=null) {
    try {
        const newItemStack = new Item(ItemID).getItemStack();
        newItemStack.func_77964_b(damage);  //setItemDamage
        if(nbttag != null) {
            if(typeof nbttag === "string") {
                const newNBTTag = JsonToNBT.func_180713_a(nbttag);  //getTagFromJson
                newItemStack.func_77982_d(newNBTTag);  //setTagCompound
            } else newItemStack.func_77982_d(nbttag);  //setTagCompound
        }
        return newItemStack;
    } catch (error) {
        return null;
    }
}

/**
 * 从NotEnoughUpdates的REPO中获取物品并转换成ItemStack
 * @param {string} name 物品的SkyBlock ID
 * @returns {MCTItemStack | null} 若未找到此物品repo或转换失败则返回 null
 */
export function getItemFromNEUREPO(name) {
    const location = "./config/notenoughupdates/repo/items/" + name + ".json";
    if(!FileLib.exists(location)) return null;
    try {
        const repo = JSON.parse(FileLib.read(location));
        return newItemStackFrom(repo.itemid,repo.damage,repo.nbttag);
    } catch (error) {
        return null;
    }
}


export const EssentialAPI = Java.type("gg.essential.api.EssentialAPI");
/**
 * 使用EssentialAPI发送一条提示信息
 * @param {string} title 信息标题
 * @param {string} noti 信息内容
 * @param {number} time 展示时间
 * @param {function} action 点击后执行的函数
 * @param {function} closeAction 关闭后执行的函数
 */
export function sendEssentialNotifications (title,noti,time=4,action=undefined,closeAction=undefined) {
    if(typeof action === "function") {
        if(typeof closeAction === "function") {
            EssentialAPI.getNotifications().push(title,noti,time,action,closeAction);
        } else EssentialAPI.getNotifications().push(title,noti,time,action);
    } else EssentialAPI.getNotifications().push(title,noti,time);
}

const NBTTagString = Java.type("net.minecraft.nbt.NBTTagString");
/**
 * 为物品添加或修改lore
 * @param {Item} item 要修改lore的物品
 * @param {string} str 添加或覆盖上去的文本 留空则会在覆盖时删除该行
 * @param {string} replaceStr 被覆盖的那行中包含的文本
 */
export function addLore(item,str,replaceStr=null) {
    try {
        if(!item) return;
        const list = new NBTTagList(
            item
              .getNBT()
              .getCompoundTag("tag")
              .getCompoundTag("display")
              .getTagMap()
              .get("Lore")
        );
        if(!list) return;
        if(replaceStr === null) {
            if(!list.toArray().join('\n').includes(str)) list.appendTag(new NBTTagString(str));
            return;
        }
        for (let i = 0; i < list.getTagCount(); i++) {
            if(list.getStringTagAt(i) && list.getStringTagAt(i).removeFormatting().includes(replaceStr)) {
                if(str != "") list.set(i,new NBTTagString(str));
                else list.removeTag(i);
            }
        }
    } catch (error) { }
}

const MCNBTTagCompound = Java.type("net.minecraft.nbt.NBTTagCompound");
/**
 * 获取实体的生命值和最大生命值
 * @param {Entity} entity 
 */
export function getEntityHP(entity) {
    let MCNBTTag = new MCNBTTagCompound();
    entity.getEntity().func_70109_d(MCNBTTag);  //writeToNBT
    let CTNBTTagObject = new NBTTagCompound(MCNBTTag).toObject();
    if(CTNBTTagObject.Attributes == undefined) return {hp:0,maxhp:0};
    return {hp:CTNBTTagObject.HealF,maxhp:CTNBTTagObject.Attributes.filter(e => e && e.Name == "generic.maxHealth")[0].Base};
}

/**
 * 包含一些设置玩家视角方向的函数
 */
export const Angle = {
    /**
     * 获取玩家眼睛的位置到目标坐标的角度
     * @param {number} x 目标点的x坐标
     * @param {number} y 目标点的y坐标
     * @param {number} z 目标点的z坐标
     * @returns 
     */
    getAngleWithPos(x, y, z) {
        let yaw = Math.atan2(Player.getZ() - z, Player.getX() - x) * 180 / Math.PI + 90;
        let pitch = Math.atan2(Player.getY() + Player.getPlayer().func_70047_e() - y, Math.sqrt((Player.getX() - x)**2 + (Player.getZ() - z)**2)) * 180 / Math.PI;
        return {yaw : yaw, pitch : pitch};
    },

    /**
     * 获取玩家眼睛的位置到目标实体或坐标的角度
     * @param {Entity | BlockPos | Vec3i | Vec3} entity 
     * @param {boolean} eye 如果是实体,是否使用眼睛的高度
     * @returns 
     */
    getAngleWithEntity(entity, eye = false) {
        let y = eye && entity?.getEntity() ? entity.getY() + entity.getEntity().func_70047_e() : entity.getY();
        return this.getAngleWithPos(entity.getX(), y, entity.getZ());
    },

    /**
     * 设置玩家的视角方向
     * @param {Number} yaw 偏航角 -180 - 180
     * @param {Number} pitch 俯仰角 -90 - 90
     */
    setAngle(yaw, pitch) {
        if(isNaN(yaw)) yaw = Player.getYaw();
        if(isNaN(pitch)) pitch = Player.getPitch();
        Player.getPlayer().field_70177_z = yaw;  //Entity.rotationYaw
        Player.getPlayer().field_70125_A = pitch;  //Entity.rotationPitch
    },

    /**
     * 让玩家看向指定坐标
     * @param {number} x 目标点的x坐标
     * @param {number} y 目标点的y坐标
     * @param {number} z 目标点的z坐标
     */
    lookAt(x, y, z) {
        let angle = this.getAngleWithPos(x, y, z);
        this.setAngle(angle.yaw, angle.pitch);
    }
}


/**
 * 在屏幕上写文字
 * @param {String} string 要写的字 
 * @param {number} x X 坐标
 * @param {number} y Y 坐标
 * @param {number} s 字的缩放比例
 * @param {boolean} shadow 是否带阴影
 */
export function DrawString(string,x,y,s,shadow = false) {
    Renderer.scale(s);
    Renderer.drawString(string,x / s,y / s,shadow);
}


/**获取指针指着的方块的坐标, 
 * rayTrace from  https://www.chattriggers.com/modules/v/ruler  made by debug 
 * @param {number} dist 
 * @returns {Vec3i | null}
 */
export function rayTrace(dist) {
    let rayBlockPos = Player.getPlayer().func_174822_a(dist, Tessellator.partialTicks)?.func_178782_a();
    
    let rayx = rayBlockPos.func_177958_n();
    let rayy = rayBlockPos.func_177956_o();
    let rayz = rayBlockPos.func_177952_p();
  
    let blockAt = World.getBlockAt(rayx, rayy, rayz);
  
    return blockAt.getType().getRegistryName() === "minecraft:air" ? null : new Vec3i( rayx, rayy, rayz );
}


let FakeBanList = {};
/**
 * 虚假的禁令
 * @param {Number} time 持续时间 (分钟)
 */
export function FakeBan(time) {
    FakeBanList[Player.getUUID()] = {
        time : Date.now() + parseInt(time) * 1000 * 60,
        timestamp : Date.now(),
        banID : Math.random().toString(36).slice(-8).toUpperCase()
    }
}

register("tick",() => {
    if(FakeBanList[Player.getUUID()] == undefined) return;
    if(FakeBanList[Player.getUUID()].time < Date.now()) {
        delete FakeBanList[Player.getUUID()];
        return;
    }
    if(!Server.getMOTD().removeFormatting().toLowerCase().includes("hypixel") && !Server.getIP().toLowerCase().includes("hypixel")) return;
    let TimeLeft = new Date(60 * 60 * 1000 - (Date.now() - FakeBanList[Player.getUUID()].timestamp));
    let BanText = new TextComponent(
        "§cYou are temporarily banned for §r89d 23h §r" + TimeLeft.getMinutes() + "m " + TimeLeft.getSeconds() + "s" + " §cfrom this server!" +
        "\n\n§7Reason: §rCheating through the use of unfair game advantages." + 
        "\n§7Find out more: §b§nhttps://www." + "§b§nhypixel." + "§b§nnet/appeal" + 
        "\n\n§7Ban ID: §r#" + FakeBanList[Player.getUUID()].banID + 
        "\n§7Sharing your Ban ID may affect the processing of your appeal!"
    );
    Client.getMinecraft().func_147114_u().func_147298_b().func_150718_a(BanText.chatComponentText);
})


/**
 * 在指定坐标画框
 * @param {number} x 
 * @param {number} y 
 * @param {number} z 
 * @param {number} colorR 
 * @param {number} colorG 
 * @param {number} colorB 
 * @param {number} colorAlpha 
 * @param {number} widthX 
 * @param {number} widthZ 
 * @param {number} height 
 * @param {number} lineWidth 
 * @param {boolean} phase 
 */
export function drawBoxAtPos(x,y,z,colorR,colorG,colorB,colorAlpha,widthX=1,widthZ=1,height=1,lineWidth=2,phase=false) {
    Tessellator.blendFunc(770,771)
    Tessellator.enableBlend();
    GL11.glLineWidth(lineWidth);
    if(phase) Tessellator.disableDepth();
    Tessellator.disableTexture2D();
    GlStateManager.func_179094_E();

    Tessellator.begin(GL11.GL_LINE_STRIP).colorize((colorR <= 1 ? colorR : colorR / 255),(colorG <= 1 ? colorG : colorG / 255),(colorB <= 1 ? colorB : colorB / 255),(colorAlpha <= 1 ? colorAlpha : colorAlpha / 255));

    Tessellator.pos(x,y,z);
    Tessellator.pos(x+widthX,y,z);
    Tessellator.pos(x+widthX,y,z+widthZ);
    Tessellator.pos(x,y,z+widthZ);
    Tessellator.pos(x,y,z);
    Tessellator.pos(x,y+height,z);
    Tessellator.pos(x+widthX,y+height,z);
    Tessellator.pos(x+widthX,y,z);
    Tessellator.pos(x+widthX,y+height,z);
    Tessellator.pos(x+widthX,y+height,z+widthZ);
    Tessellator.pos(x+widthX,y,z+widthZ);
    Tessellator.pos(x+widthX,y+height,z+widthZ);
    Tessellator.pos(x,y+height,z+widthZ);
    Tessellator.pos(x,y,z+widthZ);
    Tessellator.pos(x,y+height,z+widthZ);
    Tessellator.pos(x,y+height,z);

    Tessellator.draw();
    
    GlStateManager.func_179121_F();
    Tessellator.enableTexture2D();
    if(phase) Tessellator.enableDepth();
    Tessellator.disableBlend();
}

/**
 * 在实体上套个框,   from  https://www.chattriggers.com/modules/v/SoopyV2
 * @param {Entity} entity 
 * @param {number} colorR 
 * @param {number} colorG 
 * @param {number} colorB 
 * @param {number} Alpha 
 * @param {number} widthX 
 * @param {number} widthZ 
 * @param {number} height 
 * @param {number} partialTicks 
 * @param {number} lineWidth 
 * @param {boolean} phase 
 */
export function drawBoxAtEntity(entity,colorR,colorG,colorB,colorAlpha=1,widthX,widthZ=widthX,height,partialTicks,lineWidth=2,phase=false){
    let x=entity.getX()+(entity.getX()-entity.getLastX())*partialTicks;
    let y=entity.getY()+(entity.getY()-entity.getLastY())*partialTicks;
    let z=entity.getZ()+(entity.getZ()-entity.getLastZ())*partialTicks;
    
    if(widthX===null){
    widthX=entity.getWidth()/2;
    widthZ=entity.getWidth()/2;
    height=entity.getHeight();
    }else{
    widthX=widthX/2;
    widthZ=widthZ/2;
    }
    
    
    GL11.glBlendFunc(770,771);
    GL11.glEnable(GL11.GL_BLEND);
    GL11.glLineWidth(lineWidth);
    if(phase)GL11.glDisable(GL11.GL_DEPTH_TEST);
    GL11.glDisable(GL11.GL_TEXTURE_2D);
    GlStateManager.func_179094_E();
    
    
    Tessellator.begin(GL11.GL_LINE_STRIP).colorize((colorR <= 1 ? colorR : colorR / 255),(colorG <= 1 ? colorG : colorG / 255),(colorB <= 1 ? colorB : colorB / 255),(colorAlpha <= 1 ? colorAlpha : colorAlpha / 255));
    
    Tessellator.pos(x+widthX,y+height,z+widthZ);
    Tessellator.pos(x+widthX,y+height,z-widthZ);
    Tessellator.pos(x-widthX,y+height,z-widthZ);
    Tessellator.pos(x-widthX,y+height,z+widthZ);
    Tessellator.pos(x+widthX,y+height,z+widthZ);
    Tessellator.pos(x+widthX,y,z+widthZ);
    Tessellator.pos(x+widthX,y,z-widthZ);
    Tessellator.pos(x-widthX,y,z-widthZ);
    Tessellator.pos(x-widthX,y,z+widthZ);
    Tessellator.pos(x-widthX,y,z-widthZ);
    Tessellator.pos(x-widthX,y+height,z-widthZ);
    Tessellator.pos(x-widthX,y,z-widthZ);
    Tessellator.pos(x+widthX,y,z-widthZ);
    Tessellator.pos(x+widthX,y+height,z-widthZ);
    Tessellator.pos(x+widthX,y,z-widthZ);
    Tessellator.pos(x+widthX,y,z+widthZ);
    Tessellator.pos(x-widthX,y,z+widthZ);
    Tessellator.pos(x-widthX,y+height,z+widthZ);
    Tessellator.pos(x+widthX,y+height,z+widthZ);
    
    Tessellator.draw();
    
    GlStateManager.func_179121_F();
    GL11.glEnable(GL11.GL_TEXTURE_2D);
    if(phase)GL11.glEnable(GL11.GL_DEPTH_TEST);
    GL11.glDisable(GL11.GL_BLEND);
}

/**
 * 在指定坐标绘制长方体
 * @param {number} x 
 * @param {number} y 
 * @param {number} z 
 * @param {number} colorR 
 * @param {number} colorG 
 * @param {number} colorB 
 * @param {number} colorAlpha 
 * @param {number} widthX 
 * @param {number} widthZ 
 * @param {number} height 
 * @param {boolean} phase 
 */
export function drawQuadsAtPos(x,y,z,colorR,colorG,colorB,colorAlpha,widthX=1,widthZ=1,height=1,phase=false) {
    GL11.glDisable(GL11.GL_CULL_FACE);
    Tessellator.blendFunc(770,771)
    Tessellator.enableBlend();
    if(phase) Tessellator.disableDepth();
    Tessellator.depthMask(false);
    Tessellator.disableTexture2D();
    GlStateManager.func_179094_E();
    
    Tessellator.begin(GL11.GL_QUADS,false).colorize((colorR <= 1 ? colorR : colorR / 255),(colorG <= 1 ? colorG : colorG / 255),(colorB <= 1 ? colorB : colorB / 255),(colorAlpha <= 1 ? colorAlpha : colorAlpha / 255));

    Tessellator.pos(x,y,z);
    Tessellator.pos(x+widthX,y,z);
    Tessellator.pos(x+widthX,y,z+widthZ);
    Tessellator.pos(x,y,z+widthZ);

    Tessellator.pos(x,y,z);
    Tessellator.pos(x,y+height,z);
    Tessellator.pos(x+widthX,y+height,z);
    Tessellator.pos(x+widthX,y,z);
 
    Tessellator.pos(x,y,z);
    Tessellator.pos(x,y+height,z);
    Tessellator.pos(x,y+height,z+widthZ);
    Tessellator.pos(x,y,z+widthZ);

    Tessellator.pos(x+widthX,y+height,z+widthZ);
    Tessellator.pos(x,y+height,z+widthZ);
    Tessellator.pos(x,y+height,z);
    Tessellator.pos(x+widthX,y+height,z);

    Tessellator.pos(x+widthX,y+height,z+widthZ);
    Tessellator.pos(x,y+height,z+widthZ);
    Tessellator.pos(x,y,z+widthZ);
    Tessellator.pos(x+widthX,y,z+widthZ);

    Tessellator.pos(x+widthX,y+height,z+widthZ);
    Tessellator.pos(x+widthX,y+height,z);
    Tessellator.pos(x+widthX,y,z);
    Tessellator.pos(x+widthX,y,z+widthZ);

    Tessellator.draw();
    
    GL11.glEnable(GL11.GL_CULL_FACE);
    GlStateManager.func_179121_F();
    Tessellator.enableTexture2D();
    if(phase) Tessellator.enableDepth();
    Tessellator.depthMask(true);
    Tessellator.disableBlend();
}

/**
 * 在指定实体绘制长方体
 * @param {Entity} entity 
 * @param {number} colorR 
 * @param {number} colorG 
 * @param {number} colorB 
 * @param {number} colorAlpha 
 * @param {number} widthX 
 * @param {number} widthZ 
 * @param {number} height 
 * @param {number} phase 
 */
export function drawQuadsAtEntity(entity,colorR,colorG,colorB,colorAlpha,widthX=null,widthZ=null,height=null,phase=false) {
    if(widthX==null) widthX = entity.getWidth();
    if(widthZ==null) widthZ = entity.getWidth();
    if(height==null) height = entity.getHeight();
    drawQuadsAtPos(entity.getRenderX()-widthX/2,entity.getRenderY(),entity.getRenderZ()-widthZ/2,colorR,colorG,colorB,colorAlpha,widthX,widthZ,height,phase);
}

//party list
export let partylist = {
    functions: [],
    sendingRequest: false,
    lastGet : null,
    lastGetTime : 0,
    /**
     * 获取一次队伍列表
     * @param {Function | undefined} func 查完以后执行的function
     */
    get(func = undefined) {
        if(this.lastGetTime + 30000 > Date.now()) {
            if(func != undefined) func(this.lastGet);
            return;
        }
        if(func != undefined) this.functions.push(func);
        if(this.sendingRequest) return;
        HypixelModAPI.requestPartyInfo();
        this.sendingRequest = true;
    }
};

HypixelModAPI.on("partyInfo", (party) => {
    partylist.functions.forEach(func => func(party));
    partylist.functions = [];
    partylist.sendingRequest = false;
    partylist.lastGet = party;
    partylist.lastGetTime = Date.now();
})

register("serverDisconnect", () => {
    partylist.leader = false;
    partylist.members = [];
    partylist.functions = [];
    partylist.sendingRequest = false;
    partylist.lastGet = null;
    partylist.lastGetTime = 0;
})

register("chat", (p1, p2) => {
    partylist.lastGet = null;
    partylist.lastGetTime = 0;
}).setChatCriteria(/The party was transferred to (.+) by (.+)/)

register("chat", (p2, message1, p1) => {
    partylist.lastGet = null;
    partylist.lastGetTime = 0;
}).setChatCriteria(/(.+)( has promoted |将组队移交给了|已将)(.+)( to Party Leader||提拔为组队队长，(.+)现在为组队管理员)/)

register("chat", () => {
    partylist.lastGet = null;
    partylist.lastGetTime = 0;
}).setChatCriteria(/(You left the party.|你离开了组队。|You have been kicked from the party by (.+) |你已被(.+)踢出组队|The party was disbanded because all invites expired and the party was empty.|因组队中没有成员， 且所有邀请均已过期， 组队已被解散。|(.+) has disbanded the party!|(.+)解散了组队！)/)

export let waypoints = {
    waypoints : [],
    
    /**
     * 添加一个坐标点
     * @param {string} name 名字
     * @param {number} x x 坐标
     * @param {number} y y 坐标
     * @param {number} z z 坐标
     * @param {number} time 标点的持续时间(s) 填-1或不填则只会在重载时清除 填0会在世界加载时清除
     * @param {boolean} cmd 是否为指令添加,如果是则会在聊天栏显示 已添加坐标点 消息
     */
    addWaypoint(name,x,y,z,time=-1,cmd=false) {
        this.waypoints.push({name:name.replace(/&/g,"§"),x:x,y:y,z:z,time:time*1000,timestamp:Date.now()});
        if(cmd) new Message(new TextComponent("§6§l[BZA] §a已在 §b" + x + " " + y + " " + z + " §a处添加标记点 §r" + name).setClick("run_command","/" + settings.modulecmd + " delwaypoint " + name.removeFormatting()).setHover("show_text","§d坐标点加错了? 点击即可删除这个坐标点")).chat();
    },

    /**
     * 删除一个已有的坐标点
     * @param {string} name 
     */
    deleteWaypoint(name) {
        let del = this.waypoints.filter(e => e.name.removeFormatting().toLowerCase() == name.removeFormatting().toLowerCase()).length;
        this.waypoints = this.waypoints.filter(e => e.name.removeFormatting().toLowerCase() != name.removeFormatting().toLowerCase());
        if(del) ChatLib.chat("§6§l[BZA] §a删除了§b" + del + "§a个名为 §b" + name + " §a的坐标点");
        else ChatLib.chat("§6§l[BZA] §e没有找到名为 §b" + name + " §e的坐标点")
    }
}

register("worldLoad",()=>{waypoints.waypoints = waypoints.waypoints.filter(e => e.time != 0)})
register("step",()=>{waypoints.waypoints = waypoints.waypoints.filter(e => e.time <= 0 || e.timestamp + e.time > Date.now())}).setDelay(1)

register("renderWorld", () => {
    if(waypoints.waypoints.length != 0) waypoints.waypoints.forEach(e => {
        Tessellator.drawString(e.name + " §6(" + parseInt(getPlayerPos().distance(new Vec3(e.x,e.y,e.z))) + "m)",Math.floor(e.x)+0.5,Math.floor(e.y)+0.5,Math.floor(e.z)+0.5,0,true,0.8,true);
        drawBoxAtPos(Math.floor(e.x),Math.floor(e.y),Math.floor(e.z),0,1,0,1);
    });
})


//get skull textures
export function getskulltextures(skull) {
    if(skull == "1xitem.skull@3" ) return new Item(skull).getNBT().getCompoundTag("tag").getCompoundTag("SkullOwner").getCompoundTag("Properties").getRawNBT().func_150295_c("textures",10).func_150305_b(0).func_74779_i("Value");
    return null;
}

export function getskulltexturesfromdropitem(entities){
    if(entities.getEntity().class.getTypeName() == "net.minecraft.entity.item.EntityItem" && entities.getName() == "item.item.skull.char") return new Item(entities).getNBT().getCompoundTag("tag").getCompoundTag("SkullOwner").getCompoundTag("Properties").getRawNBT().func_150295_c("textures",10).func_150305_b(0).func_74779_i("Value");
    return null;
}

/**
 * 从物品中获取词条
 * @param {Item} item 
 * @returns {object | null}
 */
export function GetAttributesFrom(item) {
    if(item.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getCompoundTag("attributes").hasNoTags()) return null;
    return item.getNBT().getCompoundTag("tag").getCompoundTag("ExtraAttributes").getCompoundTag("attributes").toObject();
}

/**
 * 获取所有实体并在聊天栏显示实体的信息
 * @param {number} range 获取实体的范围
 */
export function getEntities(range=0) {
    World.getAllEntities().filter(e => !range || getPlayerPos().distance(getEntityPos(e)) < range).forEach(e=>{
        try {
        new TextComponent(e.getName() + " §7-§r " + e.getClassName() + " §7-§r " 
            + e.getEntity().class.getTypeName()).setHoverValue(
            "§5X§7:§r " + e.getRenderX() + " §5Y§7:§r " + e.getRenderY() + " §5Z§7:§r " + e.getRenderZ() + '\n' +
            "§dHP§7:§r " + getEntityHP(e).hp + " §7/§r " + getEntityHP(e).maxhp + "   §dRotation§7:§r " + e.getYaw() + ' / ' + e.getPitch() + '\n' +
           (e.getRiding() ? "§b骑着 §r" + e.getRiding().getName() + " §7-§r " + e.getRiding().getEntity().class.getTypeName() : "§b没骑着什么") + '\n' +
           (e.getRider() ? "§b被 §r" + e.getRider().getName() + " §7-§r " + e.getRider().getEntity().class.getTypeName() + " §b骑着" : "§b没被什么骑着") + '\n' +
           "§e点击尝试获取详情")
        .setClick("run_command","/babyzombieaddons copyentity " + e.getUUID().toString()).chat();
        if(e.getEntity().class.getTypeName() == "net.minecraft.entity.item.EntityItem" && e.getName() == "item.item.skull.char"){
            ChatLib.chat("↑ 该物品的头颅材质值§7: \n§e" + getskulltexturesfromdropitem(e))
        }
        } catch (error) { ChatLib.chat(error) };
    })
}

/**
 * 获取所有方块实体并在聊天栏显示方块实体的信息
 * @param {number} range 获取方块实体的范围
 */
export function getTileEntity(range=0) {
    World.getAllTileEntities().filter(e => !range || getPlayerPos().distance(e.getBlockPos()) < range).forEach(e=>{
        try {
            new TextComponent(e.getBlockType().getRegistryName() + " §7-§r " + e.getBlockType().getUnlocalizedName() + " §7-§r " 
            + e.tileEntity.class.getTypeName()).setHoverValue(
                "§5loc: " + e.getBlockPos().toString()
            ).chat();
        } catch (error) { ChatLib.chat(error) };
    })
}

/**
 * 获取指定实体的NBTTAG并复制到剪贴板
 * @param {string} entityUUID 指定实体的本地UUID
 */
export function copyEntityNBT(entityUUID) {
    const entity = World.getAllEntities().filter(e => e.getUUID().toString() == entityUUID);
    if(entity.length == 0) {
        ChatLib.chat("§6§l[BZA-test] §c找不到该实体");
        return;
    }
    let MCNBTTag = new MCNBTTagCompound();
    entity[0].getEntity().func_70109_d(MCNBTTag);  //writeToNBT
    let CTNBTTagObject = new NBTTagCompound(MCNBTTag).toObject();
    copyToClipboard(JSON.stringify(CTNBTTagObject,null,4),"§6§l[BZA-test] §a成功复制该实体NBTTAG到剪贴板");
}

//查拍卖行所有物品并筛选出带属性词条的
const ByteArrayInputStream = Java.type("java.io.ByteArrayInputStream")
const Base64 = Java.type("java.util.Base64")
const CompressedStreamTools = Java.type("net.minecraft.nbt.CompressedStreamTools")
function ItemBytesToItemObj(bytes) {
    if(bytes) return new NBTTagCompound(CompressedStreamTools.func_74796_a(new ByteArrayInputStream(Base64.getDecoder().decode(bytes)))).toObject().i[0]
    return null;
}
function GetAttributesFromItemObj(item) {
    if(item.tag && item.tag.ExtraAttributes && item.tag.ExtraAttributes.attributes) return item.tag.ExtraAttributes.attributes;
    return null;
}

// 导出Auctions对象，用于存储AH上带词条物品的数据
export let Auctions = {
    // 上次更新时间
    lastUpdated : 0,
    // 物品和属性
    ItemWithAtt : [],
    // 加载中
    loading : false,
    // 取消查询
    cancel : false,
    // 取消查询
    cancelUpdate() {
        this.cancel = true;
    },
    // 保存数据
    save(auto = false) {
        if(!this.lastUpdated) {
            if(!auto) ChatLib.chat("§6§l[BZA] §c暂无AH上带词条物品的数据,无法保存");
            return;
        }
        try {
            const time = new Date(this.lastUpdated);
            const fileLocation = "./config/ChatTriggers/modules/BabyzombieAddons/config/AuctionsWishAtt/" + (auto ? "latest" : time.getFullYear() + '-' + (time.getMonth() + 1) + '-' + time.getDate() + '_' + time.getHours() + '.' + (time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes()) + '.' + (time.getSeconds() < 10 ? '0' + time.getSeconds() : time.getSeconds())) + ".json";
            FileLib.write(fileLocation,JSON.stringify({timestamp : this.lastUpdated, items : this.ItemWithAtt},null,4),true);
            if(!auto) ChatLib.chat("§6§l[BZA] §aAH上带词条物品的数据已保存到§7:§b " + fileLocation);
        } catch (error) {
            if(!auto) ChatLib.chat(error);
        }
    },
    // 从文件中读取数据
    load(filename = "latest") {
        try {
            if(filename != "latest" && !FileLib.exists("./config/ChatTriggers/modules/BabyzombieAddons/config/AuctionsWishAtt/" + filename + ".json")) {
                ChatLib.chat("§6§l[BZA] §c未找到名为§7: §b" + filename + " §c的AH上带词条物品的数据文件");
                return;
            }
            const fileLocation = "./config/ChatTriggers/modules/BabyzombieAddons/config/AuctionsWishAtt/" + filename + ".json";
            const data = JSON.parse(FileLib.read(fileLocation));
            this.lastUpdated = data.timestamp;
            this.ItemWithAtt = data.items;
        } catch (error) {
            if(filename != "latest") ChatLib.chat(error);
        }
    },
    // 获取AH上带词条物品的数据
    getAuctions : (page,cmd) => request(`https://api.hypixel.net/v2/skyblock/auctions?page=${page}`).then(a => JSON.parse(a)).catch(e => {
        if(settings.AuctionsRequestNotification && ((settings.AuctionsRequestNotification == 1.0 && cmd) || settings.AuctionsRequestNotification == 2.0)) new Message(`§6§l[BZA] §c查询AH失败,原因: \n§4${e}`).setChatLineId(MessageID.UpdateAuctions).chat();
        return {success : false};
        }),
    // 从Hypixel API获取所有AH上带词条物品的数据
    getAllAuctionsFromHypixelAPI : (cmd = false) => Auctions.getAuctions(0,cmd).then(e=>{
        if(Auctions.loading) return;
        if(!e.success) {
            if(settings.AuctionsRequestNotification && ((settings.AuctionsRequestNotification == 1.0 && cmd) || settings.AuctionsRequestNotification == 2.0)) new Message("§6§l[BZA] §c查询AH失败" + (e.cause ? ",原因: \n§4" + e.cause : "")).setChatLineId(MessageID.UpdateAuctions).chat();
            return;
        }
        if(cmd && Auctions.lastUpdated == e.lastUpdated) {
            ChatLib.chat("§6§l[BZA] §a当前AH上带词条物品的数据已是最新");
            return;
        }
        let StartTime = Date.now();
        let items = [];
        let totalPages = e.totalPages;
        Auctions.loading = true;
        if(settings.AuctionsRequestNotification && ((settings.AuctionsRequestNotification == 1.0 && cmd) || settings.AuctionsRequestNotification == 2.0))
            new Message('§6§l[BZA] §a准备开始查询AH,共§b' + e.totalPages + '§a页').setChatLineId(MessageID.UpdateAuctions).chat();
        for (let i = 0; i < totalPages; i++) {
            let web = "https://api.hypixel.net/v2/skyblock/auctions?page=" + i;
            if(settings.AuctionsRequestNotification && ((settings.AuctionsRequestNotification == 1.0 && cmd) || settings.AuctionsRequestNotification == 2.0))
                new Message(('§6§l[BZA] §a正在查询AH,第§e' + (i+1) + '§a页,共§b' + e.totalPages + '§a页.目前用时:' + parseInt((Date.now() - StartTime) / 1000) + '秒,已找到' + items.length + '件带词条物品   '),new TextComponent("§7[§r§c停止查询§7]").setClick("run_command","/babyzombieaddons ap cancel")).setChatLineId(MessageID.UpdateAuctions).chat();
            try {
                let auctionsFromHypixelAPI = JSON.parse(FileLib.getUrlContent(web));
                if(!auctionsFromHypixelAPI.success) {
                    if(settings.AuctionsRequestNotification && ((settings.AuctionsRequestNotification == 1.0 && cmd) || settings.AuctionsRequestNotification == 2.0)) new Message("§6§l[BZA] §c查询AH失败" + (e.cause ? ",原因: \n§4" + e.cause : "")).setChatLineId(MessageID.UpdateAuctions).chat();
                    Auctions.loading = false;
                    Auctions.cancel = false;
                    return;
                }
                auctionsFromHypixelAPI.auctions.filter(e => e.bin && GetAttributesFromItemObj(ItemBytesToItemObj(e.item_bytes))).forEach(e => {
                    const item = ItemBytesToItemObj(e.item_bytes);
                    items.push({ahid:e.uuid,id:item.tag.ExtraAttributes.id,name:item.tag.display.Name,lore:e.item_lore,price:e.starting_bid,att:GetAttributesFromItemObj(item)})
                })
                totalPages = auctionsFromHypixelAPI.totalPages;
            } catch (error) {
                if(settings.AuctionsRequestNotification && ((settings.AuctionsRequestNotification == 1.0 && cmd) || settings.AuctionsRequestNotification == 2.0)) new Message(`§6§l[BZA] §c查询AH失败,原因: \n§4${error}`).setChatLineId(MessageID.UpdateAuctions).chat();
                Auctions.loading = false;
                Auctions.cancel = false;
                return;
            }
            if(Auctions.cancel) i = totalPages;
        }
        if(Auctions.cancel) {
            new Message("§6§l[BZA] §e已停止查询AH").setChatLineId(MessageID.UpdateAuctions).chat();
            Auctions.loading = false;
            Auctions.cancel = false;
            return;
        }
        if(settings.AuctionsRequestNotification && ((settings.AuctionsRequestNotification == 1.0 && cmd) || settings.AuctionsRequestNotification == 2.0)) 
            new Message('§6§l[BZA] §aAH查询完成,用时§b' + parseInt((Date.now() - StartTime) / 1000) + '秒§a,共§b' + e.totalPages + '§a页,§b' + e.totalAuctions + "§a件物品,正在根据价格排序").setChatLineId(MessageID.UpdateAuctions).chat();
        items.sort((a,b) => {return a.price - b.price});
        if(settings.AuctionsRequestNotification && ((settings.AuctionsRequestNotification == 1.0 && cmd) || settings.AuctionsRequestNotification == 2.0)) 
            new Message('§6§l[BZA] §aAH查询完成,用时§b' + parseInt((Date.now() - StartTime) / 1000) + '秒§a,共§b' + e.totalPages + '§a页,§b' + items.length + "§a件带词条物品").setChatLineId(MessageID.UpdateAuctions).chat()
        Auctions.loading = false;
        Auctions.lastUpdated = e.lastUpdated;
        Auctions.ItemWithAtt = items.concat([]);
        Auctions.save(true);
        return Auctions.ItemWithAtt;
    }).catch(e=> {if(settings.AuctionsRequestNotification && ((settings.AuctionsRequestNotification == 1.0 && cmd) || settings.AuctionsRequestNotification == 2.0)) ChatLib.chat(`§6§l[BZA] §c查询AH失败,原因: \n§4${e}`)}),

    //手动获取auctions
    getAuction : false,
    getAuctionConfirm : false,

    get() {
        if(!FileLib.isDirectory("./config/ChatTriggers/modules/BabyzombieAddons/config/AuctionsWishAtt/auctions")) {
            FileLib.write("./config/ChatTriggers/modules/BabyzombieAddons/config/AuctionsWishAtt/auctions/createDirectory","如果你看到这个,就把这个文件删了",true);
            FileLib.delete("./config/ChatTriggers/modules/BabyzombieAddons/config/AuctionsWishAtt/auctions/createDirectory");
        }
        if(Auctions.lastUpdated + 2 * 60 * 1000 > Date.now()){
            new TextComponent(
                "§6§l[BZA] §a两分钟内刚查过,还查?"
            ).chat();
            return;
        }
        openFolder("./config/ChatTriggers/modules/BabyzombieAddons/config/AuctionsWishAtt/auctions");
        openBrowse("https://api.hypixel.net/v2/skyblock/auctions?page=0");
        this.getAuction = true;
        this.getAuctionConfirm = false;
        new TextComponent(
            "§6§l[BZA] §a已为您打开auctions文件夹和hypixel api页面\n§a请先在文件夹内创建一个txt或json文件\n§a然后将打开的网页中所有内容复制到刚创建的文件内并保存\n§a再在网页网址的最后面将数字加大1点\n§a并重复此过程直到ah所有页都被复制到本地文件\n完成后输入 §6/bza ap getconfirm §a即可完成手动查询"
        ).chat();
    },

    getConfirm() {
        if(!this.getAuction || !FileLib.isDirectory("./config/ChatTriggers/modules/BabyzombieAddons/config/AuctionsWishAtt/auctions")) {this.get();return;}
        let auctionsDirectory = new File("./config/ChatTriggers/modules/BabyzombieAddons/config/AuctionsWishAtt/auctions");
        switch (true) {
            case (auctionsDirectory.listFiles().length < 10):
                new TextComponent(
                    "§6§l[BZA] §e嗯?auctions文件夹内的文件都不到10个,ah真的就这么几页吗\n§e如果是真的就创建几个空文件吧~"
                ).chat();
                break;
            case (!this.getAuctionConfirm) :
                new TextComponent(
                    "§6§l[BZA] §a以获取到文件夹内的 " + auctionsDirectory.listFiles().length + " 个文件\n§a确定这是auctions所有的页吗?\n§a再次输入 §6/bza ap getconfirm §a确认并覆盖当前保存的数据"
                ).chat();
                this.getAuctionConfirm = true;
                break;
            default:
                let files = auctionsDirectory.listFiles();
                if(!files.every(file => {
                    let data = FileLib.read(file);
                    let json = JSON.parse(data);
                    return json && json.success;
                })) {
                    new TextComponent(
                        "§6§l[BZA] §cauctions文件夹内的文件有部分是无效文件,请检查"
                    ).chat();
                    return;
                }
                if(JSON.parse(FileLib.read(files[0])).totalPages > files.length) {
                    new TextComponent(
                        "§6§l[BZA] §c文件内的页数比api返回的少,请补充"
                    ).chat();
                    return;
                }
                let items = [];
                let lastUpdated = 0;
                try {
                    files.forEach(file => {
                        let data = FileLib.read(file);
                        let json = JSON.parse(data);
                        json.auctions.filter(e => e.bin && GetAttributesFromItemObj(ItemBytesToItemObj(e.item_bytes))).forEach(auction => {
                            const item = ItemBytesToItemObj(e.item_bytes);
                            items.push({ahid:e.uuid,id:item.tag.ExtraAttributes.id,name:item.tag.display.Name,lore:e.item_lore,price:e.starting_bid,att:GetAttributesFromItemObj(item)});
                        })
                        if(lastUpdated < json.lastUpdated) lastUpdated = json.lastUpdated;
                    })
                    items.sort((a,b) => {return a.price - b.price});
                    Auctions.loading = false;
                    this.getAuction = false;
                    this.getAuctionConfirm = false;
                    Auctions.lastUpdated = lastUpdated;
                    Auctions.ItemWithAtt = items.concat([]);
                    Auctions.save(true);
                    new Message('§6§l[BZA] §b' + files.length + '§a个文件解析完成,共§b' + items.length + "§a件带词条物品\n" +
                        new TextComponent("§c点击把刚用完的文件都删了").setClick("run_command","/babyzombieaddons ap deleteauctionfiles")).chat();
                } catch (error) {
                    ChatLib.chat("§6§l[BZA] §c文件解析错误,以下是报错:\n" + error);
                }
                break;
        }
    },

    deleteAuctionFiles() {
        if(!FileLib.isDirectory("./config/ChatTriggers/modules/BabyzombieAddons/config/AuctionsWishAtt/auctions")) {
            FileLib.write("./config/ChatTriggers/modules/BabyzombieAddons/config/AuctionsWishAtt/auctions/createDirectory","如果你看到这个,就把这个文件删了",true);
            FileLib.delete("./config/ChatTriggers/modules/BabyzombieAddons/config/AuctionsWishAtt/auctions/createDirectory");
        }
        let auctionsDirectory = new File("./config/ChatTriggers/modules/BabyzombieAddons/config/AuctionsWishAtt/auctions");
        auctionsDirectory.listFiles().forEach(file => {
            file.delete();
        })
        ChatLib.chat("§6§l[BZA] §a已删除auctions文件夹内的文件");
    }
}

register("step", () => {   //如果开启,就时不时重新获取一下ah
    if(settings.AuctionsRequest != 1.0 && !Auctions.loading && (!Auctions.lastUpdated || ( settings.AuctionsRequest == 2.0 && Auctions.lastUpdated && Date.now() - Auctions.lastUpdated > settings.AuctionsRequestTime * 60 * 1000 ) ) ) Auctions.getAllAuctionsFromHypixelAPI();
}).setDelay(60)
Auctions.load();


//api相关功能
const getuuid = (player) => request(`https://api.mojang.com/users/profiles/minecraft/${player}`).then(a => JSON.parse(a)).catch(e => null);
const getHypixelPlayerStatus = (uuid , apikey) => request(`https://api.hypixel.net/v2/status?key=${apikey}&uuid=${uuid}`).then(a => JSON.parse(a)).catch(e => null);
const getSBProfiles = (uuid , apikey) => request(`https://api.hypixel.net/v2/skyblock/profiles?key=${apikey}&uuid=${uuid}`).then(a => JSON.parse(a)).catch(e => null);

//获取玩家状态
AddData("apiKey","");
let PlayerStatusCMDCooldown = {};
export function getPlayerStatus(player) {
    getuuid(player).then(uuid => {
        if(!uuid){return ChatLib.chat("§6§l[BZA] §4未找到该玩家")};
        const {name , id} = uuid;
        if(PlayerStatusCMDCooldown[name] && Date.now() - PlayerStatusCMDCooldown[name] < 60 * 1000) {
            return ChatLib.chat(new Message("§cWow,slow down!").setRecursive(true));
        }
        getHypixelPlayerStatus(id , SavedData.apikey).then(text => {
            if(!text) return ChatLib.chat("§6§l[BZA] §4未能获取到该玩家状态");
            PlayerStatusCMDCooldown[name] = Date.now();
            const status = text.session;
            if(!status.online) return ChatLib.chat(new Message("§6§l[BZA] §e" + name + "不在线").setRecursive(true));
            let mode = status.mode;
            if(status.gameType == "SKYBLOCK") {
                switch (status.mode) {
                    case "dynamic": mode = "private island"; break;
                    case "combat_1": mode = "spider's den"; break;
                    case "combat_3": mode = "the end"; break;
                    case "foraging_1": mode = "the park"; break;
                    case "mining_1": mode = "gold mine"; break;
                    case "mining_2": mode = "deep caverns"; break;
                    case "mining_3": mode = "dwarven mines"; break;
                    case "farming_1": mode = "the farming islands"; break;
                    case "instanced": mode = "kuudra"; break;
                }
            }
            ChatLib.chat(new Message("§6§l[BZA] §e" + name + " 正在" + (status.mode != "LOBBY" ? "游玩" : '') + " §b" + status.gameType + " §b" + mode + (status.map ? " §e地图§7:§b " + status.map : "")).setRecursive(true))
        })
    })
}

//get SB profiles
let SBProfileData = {};
register("worldLoad",() => {
    if(SBProfileData[Player.getName()]) {
        SBProfileData[Player.getName()].timestamp = SBProfileData[Player.getName()].timestamp - 4.5 * 60 * 1000;
    }
})
export function getSBProfile(player) {
    return getuuid(player).then(uuid => {
        if(!uuid){return ChatLib.chat("§6§l[BZA] §4未找到该玩家")};
        const {name , id} = uuid;
        if(SBProfileData[name] && Date.now() - SBProfileData[name].timestamp < 5 * 60 * 1000) {
            return SBProfileData[name];  //五分钟内请求过就用上次请求时返回的内容
        }
        return getSBProfiles(id , SavedData.apikey).then(e => {
            if(!e){return ChatLib.chat("§6§l[BZA] §4未能获取到该玩家Skyblock存档数据,这可能是网络问题")};
            let profile = e.profiles.filter(e => e.selected)[0].members[id];
            SBProfileData[name] = profile;
            SBProfileData[name].name = name;
            SBProfileData[name].timestamp = Date.now();
            return SBProfileData[name];
        });
    });
};

export function saveSBProfile(player) {
    if(SavedData.apikey.length == 0) {ChatLib.chat("§6§l[BZA] §4此功能需要api key");return;}
    getuuid(player).then(uuid => {
        if(!uuid){ChatLib.chat("§6§l[BZA] §4未找到该玩家");return;};
        const {name , id} = uuid;
        try {
            const e = JSON.parse(FileLib.getUrlContent(`https://api.hypixel.net/v2/skyblock/profiles?key=${SavedData.apikey}&uuid=${id}`));
            const profile = e.profiles.filter(e => e.selected)[0].members[id];
            FileLib.write("./config/ChatTriggers/modules/BabyzombieAddons/SBProfile/" + name + ".json",JSON.stringify(profile,null,4),true);
        } catch (error) {
            ChatLib.chat("§6§l[BZA] §4未能获取到该玩家Skyblock存档数据,请检查apikey");
            return;
        }
    })
}

//下载文件
const File = Java.type("java.io.File");
const URL = Java.type("java.net.URL");
const PrintStream = Java.type("java.io.PrintStream");
const Byte = Java.type("java.lang.Byte");
function downloadFile(url,destination,resolve,reject) {
    try {
        const d=new File(destination);
        d.getParentFile().mkdirs();
        const connection=new URL(url).openConnection();
        connection.setDoOutput(true);
        connection.setConnectTimeout(10000);
        connection.setReadTimeout(20000);
        const IS=connection.getInputStream();
        const FilePS=new PrintStream(destination);
        let buf=new Packages.java.lang.reflect.Array.newInstance(Byte.TYPE,65536);
        let len;
        while((len=IS.read(buf))>0){
            FilePS.write(buf,0,len);
        }
        IS.close();
        FilePS.close();
        resolve(destination);
    } catch (error) {
        reject(error);
    }
}
/**
 * 通过网址下载文件 (从spoopy那里看来的)
 * @param {string} url 文件来源
 * @param {string} destination 文件存放位置
 */
export function urlToFile(url,destination){
    return new Promise((resolve,reject) => downloadFile(url,destination,resolve,reject));
}

const Desktop = Java.type("java.awt.Desktop");
/**
 * 通过windows文件资源管理器打开路径
 * @param {string} destination 打开的文件夹的路径
 */
export function openFolder(destination) {
    try {
        Desktop.getDesktop().open(new File(destination));
    } catch (error) { ChatLib.chat(`§6§l[BZA] §e在打开路径时出现错误§7: \n§c${error}`) }
}

const URI = Java.type("java.net.URI");
/**
 * 通过默认浏览器打开链接
 * @param {string} uri 打开的网页链接
 */
export function openBrowse(uri) {
    try {
        Desktop.getDesktop().browse(new URI(uri));
    } catch (error) { ChatLib.chat(`§6§l[BZA] §e在打开链接时出现错误§7: \n§c${error}`) }
}

//检测更新
AddData("IgnoreVersion","");
function VersionToInt(version) {
    if(!version.includes('.')) return 0;
    const array = version.split('.');
    return array[0] * 1000000 + array[1] * 1000 + array[2];
}

export function CheckVersion(command = false) {
    let VersionJSON = {};
    try {
        VersionJSON = JSON.parse(FileLib.getUrlContent(`https://babyzombie.top:5555/skyblock/BabyzombieAddons/version.json`))
    } catch (error) {
        if(command) ChatLib.chat("§6§l[BZA] §4无法获取更新,原因 " + error);
        return;
    }
    const {version , update , logs} = VersionJSON;
    if(SavedData.IgnoreVersion == version && !command) return;
    
    const VersionNow = JSON.parse(FileLib.read("BabyzombieAddons", "metadata.json")).version;
    if(!VersionNow) {ChatLib.chat("§6§l[BZA] §4无法获取当前module版本,你这模块是怎么跑起来的?");return;}
    if(VersionToInt(version) > VersionToInt(VersionNow)) {
        if(settings.AutoCheckUpdate == 1 || command) {
            new Message(
                new TextComponent("§6§lBabyzombieAddons").setClick("open_url","https://babyzombie.top:5555").setHover("show_text","§a点击打开Module网站"), "   §r§b有新版本可用   " , 
                new TextComponent("§e" + VersionNow).setHover("show_text",(logs[VersionNow] ? logs[VersionNow] : "§7未找到此版本的更新日志")) , " §r§f→§r " ,
                new TextComponent("§a"+ version).setHover("show_text",(logs[version] ? logs[version] : "§7未找到此版本的更新日志")) , '\n' ,
                "§5§m-----------------------------------------------------§r\n" , (logs[version] ? logs[version] : "§7未找到此版本的更新日志") , "\n§5§m-----------------------------------------------------§r", "\n",
                new TextComponent("§a一键更新").setClick("run_command","/babyzombieaddons update " + version).setHover("show_text","§a一键自动下载并更新"),"   ",
                new TextComponent("§b历史更新日志").setClick("run_command","/babyzombieaddons versionlogs").setHover("show_text","§b查看其他版本的更新日志"),"   ",
                new TextComponent("§4此版本不再提醒").setClick("run_command","/babyzombieaddons ignoreupdateversion " + version)
            ).chat();
            sendEssentialNotifications(
                "§6§lBabyzombieAddons §b有新版本可用\n§e" + VersionNow + " §r§f→§r §a" + version + "   §a点击更新",
                logs[version] ? "§5§m-----------------------------------------------------§r\n" + logs[version] : "§7未找到此版本的更新日志",
                20, () => { updateModules(version) }
            )
        }
        else if(settings.AutoCheckUpdate == 2) {
            new Message(
                new TextComponent("§6§lBabyzombieAddons").setClick("open_url","https://babyzombie.top:5555").setHover("show_text","§a点击打开Module网站"), "   §r§b发现新版本,正在更新   " , 
                new TextComponent("§e" + VersionNow).setHover("show_text",(logs[VersionNow] ? logs[VersionNow] : "§7未找到此版本的更新日志")) , " §r§f→§r " ,
                new TextComponent("§a"+ version).setHover("show_text",(logs[version] ? logs[version] : "§7未找到此版本的更新日志")) , '\n' ,
                "§5§m-----------------------------------------------------§r\n" , (logs[version] ? logs[version] : "§7未找到此版本的更新日志") , "\n§5§m-----------------------------------------------------§r"
            ).chat();
            sendEssentialNotifications(
                "§6§lBabyzombieAddons §b发现新版本,正在更新\n§e" + VersionNow + " §r§f→§r §a" + version,
                logs[version] ? "§5§m-----------------------------------------------------§r\n" + logs[version] : "§7未找到此版本的更新日志", 10
            )
            updateModules(version);
        }
    } else if(command) new Message("§6§l[BZA] §b当前已是最新版本: ",new TextComponent("§a"+version).setHover("show_text",(logs[version] ? logs[version] : "§7未找到此版本的更新日志"))).chat();
}

let checkUpdate = register("worldLoad" , () => {
    setTimeout(() => {  //检测更新
        if(settings.AutoCheckUpdate) CheckVersion();
        if(isModLoaded("InputFix")) new Message("§6§l[BZA] §c检测到您安装了 §r§c§lInputFix§r \n§c此mod可能会导致ct-js的按键触发器失效,致使部分功能无法正常使用 \n§c推荐使用 ",new TextComponent("§a§lPatcher").setHover("show_text","§a带我去Patcher官网").setClick("open_url","https://sk1er.club/mods/patcher")," §c来代替此mod的功能").chat();
        checkUpdate.unregister();
    }, 10000);
})

//下载更新
export function updateModules(version) {
    const versionNow = JSON.parse(FileLib.read("BabyzombieAddons", "metadata.json")).version;
    const FileName = "BabyzombieAddons-" + version + ".zip";
    new File("./config/ChatTriggers/modules/BabyzombieAddonsTempFile").mkdir();
    new Message("§6§l[BZA] §e准备开始更新Module版本至 " + version).setChatLineId(MessageID.UpdateVersion).chat();
    urlToFile("https://babyzombie.top:5555/skyblock/BabyzombieAddons/" + FileName,"./config/ChatTriggers/modules/BabyzombieAddonsTempFile/" + FileName).then(e => {
        new Message("§6§l[BZA] §a压缩包已下载完成,正在准备解压和替换").setChatLineId(MessageID.UpdateVersion).chat();
        new File("./config/ChatTriggers/modules/BabyzombieAddons").listFiles().forEach(e => { if(e.getName() != "config") e.delete(); });
        setTimeout(() => {
            FileLib.unzip("./config/ChatTriggers/modules/BabyzombieAddonsTempFile/" + FileName,"./config/ChatTriggers/modules");
            FileLib.deleteDirectory("./config/ChatTriggers/modules/BabyzombieAddonsTempFile");
            new Message("§6§l[BZA] §a解压完成,已从 " + versionNow + " 更新至了 " + version + ",正在重载CT-JS").setChatLineId(MessageID.UpdateVersion).chat();
            ChatTriggers.reloadCT();
        }, 1000);
    }).catch(e => { new Message("§6§l[BZA] §c更新失败,原因: \n" + e).setChatLineId(MessageID.UpdateVersion).chat(); });
}


//历史更新日志
export function VersionLogs() {
    let VersionJSON = {};
    try {
        VersionJSON = JSON.parse(FileLib.getUrlContent(`https://babyzombie.top:5555/skyblock/BabyzombieAddons/version.json`))
    } catch (error) {
        if(command) ChatLib.chat("§6§l[BZA] §4无法获取更新,原因 " + error);
        return;
    }
    const versionNow = JSON.parse(FileLib.read("BabyzombieAddons", "metadata.json")).version;
    const {version , update , logs} = VersionJSON;
    ChatLib.chat(`§1§m                                                                  §r\n§6BabyzombieAddons §a各版本更新日志`);
    for (let key in logs) {
        ChatLib.chat(new TextComponent("§a" + key).setHover("show_text",logs[key] + "\n\n§7§k|§r §a点击下载并安装 " + key + " 版本的此module §7§k|§r").setClick("run_command","/babyzombieaddons update " + key))
    }
    ChatLib.chat(new Message(
        "§b当前版本: ",
        new TextComponent("§e" + versionNow).setHover("show_text",logs[versionNow] + "\n\n§7§k|§r §a点击重新安装此module §7§k|§r").setClick("run_command","/babyzombieaddons update " + versionNow),
        "     §b最新版本: ",
        new TextComponent("§a" + version).setHover("show_text",logs[version] + "\n\n§7§k|§r §a点击下载并安装 " + version + " 版本的此module §7§k|§r").setClick("run_command","/babyzombieaddons update " + version),
        `\n§1§m                                                                  `));
}